package com.gurumades.jsonform.server.forms.dispatch;

import java.io.Serializable;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;

import javax.servlet.ServletContext;

import org.codehaus.jackson.JsonNode;
import org.codehaus.jackson.node.ObjectNode;

import com.gurumades.jsonform.client.presenter.FormItemError;
import com.gurumades.jsonform.shared.dispatch.GetFormAction;
import com.gurumades.jsonform.shared.dispatch.validators.FormItemValidator;
import com.gurumades.jsonform.shared.dispatch.validators.ValidatorHelper;

import net.customware.gwt.dispatch.server.ActionHandler;
import net.customware.gwt.dispatch.server.DefaultActionHandlerRegistry;
import net.customware.gwt.dispatch.server.Dispatch;
import net.customware.gwt.dispatch.server.SimpleDispatch;
import net.customware.gwt.dispatch.shared.DispatchException;

public final class DispatchUtil {

	private static ServletContext context;

	private static Map<String, FormProvider> providers = new HashMap<String, FormProvider>();

	private static Map<String, Set<FormPreProcessor>> preProcessors = new HashMap<String, Set<FormPreProcessor>>();

	private static final DefaultActionHandlerRegistry REGISTRY = new DefaultActionHandlerRegistry();

	private static final Dispatch DISPATCH = new SimpleDispatch(REGISTRY);

	static {
		ValidatorHelper.setJsonhelper(new ServerSideJsonHelper());
	}
	
	public static void registerHandler(ActionHandler<?, ?> handler) {
		REGISTRY.addHandler(handler);
	}

	public static Dispatch getDispatch() {
		return DISPATCH;
	}

	public static ServletContext getContext() {
		return context;
	}

	public static void setContext(ServletContext context) {
		DispatchUtil.context = context;
	}

	public static Map<String, FormProvider> getFormProviders() {
		return providers;
	}

	public static void registerProvider(FormProvider p) {
		p.setServletContext(context);
		Set<String> formId = p.getForms();

		for (String id : formId) {
			providers.put(id, p);
		}

		
	}

	private static void registerPreProcessor(String formID, FormPreProcessor p) {
		Set<FormPreProcessor> list = preProcessors.get(formID);
		
		if (list == null) {
			list = new HashSet<FormPreProcessor>();
			preProcessors.put(formID, list);
		}
		
		list.add(p);
	}
	
	public static void registerPreProcessor(FormPreProcessor p) {
		String[] formIDs = p.formIDs();
		for (int i = 0; i < formIDs.length; i++) {
			registerPreProcessor(formIDs[i], p);
		}
		
	}

	public static HashMap<String, FormItemError> validateForm(String formID,
			JsonNode form, Map<String, Object> values) {
		if (form.isObject()) {
			ObjectNode theFormNode = (ObjectNode) form;
			FormPreprocessHelp helper = FormPreprocessHelpImpl.get(theFormNode
					.get("buildID").getLongValue());

			//Collection<String> itemValidators = helper.getItemValidators();

			ValidatorHelper validatorHelper = new ValidatorHelper();

			List<JsonNode> fields = theFormNode.findParents("extra");
			for (JsonNode jsonNode : fields) {
				// If this is not an object, then something is wrong and this is
				// not a field.
				// Just skip this line for now.
				if (!jsonNode.isObject()) {
					continue;
				}

				ObjectNode item = (ObjectNode) jsonNode;

				Set<FormItemValidator> validators = helper
						.getItemValidators(item.path("id").getValueAsText());

				for (FormItemValidator formItemValidator : validators) {
					if (!item.get("visible").getBooleanValue()) {
						values.remove(item.get("id"));
						continue;
					}
					formItemValidator.validate((Object) item, values,
							validatorHelper);
				}
			}

			if (validatorHelper.hasErrors()) {
				return validatorHelper.errors();
			}

			Set<FormValidator> formValidators = helper.getFormValidators();

			for (FormValidator formValidator : formValidators) {
				formValidator.validate(theFormNode, values, validatorHelper);
			}

			if (validatorHelper.hasErrors()) {
				return validatorHelper.errors();
			}

		}

		return new HashMap<String, FormItemError>();
	}

	public static SubmitHelper submitForm(String formID, JsonNode form,
			Map<String, JsonNode> values) throws DispatchException {

		if (form.isObject()) {
			ObjectNode theFormNode = (ObjectNode) form;
			FormPreprocessHelp helper = FormPreprocessHelpImpl.get(theFormNode
					.get("buildID").getLongValue());

			Set<FormSubmiter> submiters = helper.getSubmiters();

			SubmitHelper submitHelper = new SubmitHelper();
			for (FormSubmiter formSubmiter : submiters) {
				formSubmiter.submitForm(formID, theFormNode, values, submitHelper);
			}
			
			return submitHelper;
		}

		return null;
	}

	public static JsonNode preprocessForm(String formID, JsonNode form,
			boolean isBuilding, Long buildID) {
		return preprocessForm(formID, form, isBuilding, new HashMap<String, Serializable>(), buildID);
	}
	
	public static JsonNode preprocessForm(String formID, JsonNode form,
			boolean isBuilding, HashMap<String, Serializable> buildParams, Long buildID2) {
		Set<FormPreProcessor> set = getPrepocessors(formID);
		if (set == null) {
			set = new HashSet<FormPreProcessor>();
		}

		FormPreprocessHelp helper = null;
		if (isBuilding) {
			helper = new FormPreprocessHelpImpl();
			helper.setFormIsBuilding(isBuilding);
			helper.setBuildParams(buildParams);
			helper.setBuildID(buildID2);
		}else {
			helper = FormPreprocessHelpImpl.get(buildID2);
		}
		

		for (Iterator<FormPreProcessor> iterator = set.iterator(); iterator
				.hasNext();) {
			FormPreProcessor formPreProcessor = (FormPreProcessor) iterator
					.next();
			if (isBuilding) {
				form = formPreProcessor.preProcessForm(form, helper);
				continue;
			}else{
				form = formPreProcessor.preProcessFormSubmit(form, helper);
			}

			

		}

		if (form.isObject()) {
			ObjectNode theFormNode = (ObjectNode) form;
			theFormNode.put("buildID", helper.getBuildID());
		}

		FormPreprocessHelpImpl.cache(helper.getBuildID(), helper);

		return form;
	}

	private static Set<FormPreProcessor> getPrepocessors(String formID) {
		Set<FormPreProcessor> list = preProcessors.get(formID);
		
		if (list == null) {
			list = new HashSet<FormPreProcessor>();
			
			if (preProcessors.get("*") != null) {
				list.addAll(preProcessors.get("*"));
			}
			
		}
		
		return list;
	}

}